((w, doc) => {
  class Vue {
    constructor(options) {
      this._init(options);
    }
    _init(options) {
      let vm = this;
      vm.$options = options;
      initState(vm);
    }
  }
  w.Vue = Vue;
  function initState(vm) {
    let options = vm.$options;
    if (options.data) {
      initData(vm);
      Compile(options.el, vm);
    }
  }
  function initData(vm) {
    let data = vm.$options.data;
    data = vm._data = typeof data === "function" ? data.call(vm) : data || {};
    for (let k in data) {
      ProxyData(vm, "_data", k);
    }
    observe(vm._data);
  }
  function ProxyData(vm, target, k) {
    Object.defineProperty(vm, k, {
      enumerable: true,
      configurable: true,
      get() {
        return vm[target][k];
      },
      set(val) {
        vm[target][k] = val;
      },
    });
  }
  function observe(data) {
    if (!data || typeof data !== "object") return;
    return new Observer(data);
  }
  function observeArr(arr) {
    for (i = 0, len = arr.length; i < len; i++) {
      observe(arr[i]);
    }
  }
  var ARR_METHODS = ["pop", "push", "unshift", "shift", "splice"];
  var originArrMethods = Array.prototype,
    arrMethods = Object.create(originArrMethods);
  ARR_METHODS.map(function (m) {
    arrMethods[m] = function () {
      let args = Array.prototype.slice.call(arguments);
      let res = originArrMethods[m].apply(this, args);
      let newArr = null;
      switch (m) {
        case "push":
        case "unshift":
          newArr = args;
          break;
        case "splice":
          newArr = args.slice(2);
          break;
        default:
          break;
      }
      newArr && observeArr(newArr);
      return res;
    };
  });
  class Observer {
    constructor(data) {
      if (Array.isArray(data)) {
        data.__protot__ = arrMethods;
        observeArr(data);
      } else {
        this.walk(data);
      }
    }
    walk(data) {
      let keys = Object.keys(data);
      keys.forEach((k) => {
        defineReactiveData(data, k, data[k]);
      });
    }
  }
  function defineReactiveData(data, k, value) {
    const dep = new Dep();
    observe(value);
    Object.defineProperty(data, k, {
      enumerable: true,
      configurable: true,
      get() {
        Dep.target && dep.addSub(Dep.target);
        return value;
      },
      set(val) {
        if (val === value) return;
        value = val;
        dep.notify();
      },
    });
  }

  function Compile(el, vm) {
    vm.$el = doc.querySelector(el);
    let oFrag = doc.createDocumentFragment(),
      childNode = null;
    while ((childNode = vm.$el.firstChild)) {
      oFrag.appendChild(childNode);
    }
    replace(oFrag);
    function replace(node) {
      const reg = /\{\{\s*(\S+)\s*\}\}/;
      if (node.nodeType === 3) {
        const text = node.textContent;
        const execRes = reg.exec(text);
        if (execRes) {
          const key = execRes[1];
          const value = key.split(".").reduce((obj, k) => obj[k], vm);
          node.textContent = text.replace(reg, value);
          new Watcher(vm, key, (newVal) => {
            console.log(newVal);
            node.textContent = text.replace(reg, newVal);
          });
        }
        return;
      } else if (node.nodeType === 1 && node.tagName === "INPUT") {
        const attrs = Array.from(node.attributes);
        const result = attrs.find((v) => v.name === "v-model");
        if (result) {
          const key = result.value;
          const keyArr = key.split(".");
          const value = keyArr.reduce((obj, k) => obj[k], vm);
          node.value = value;
          new Watcher(vm, key, (newVal) => {
            node.value = newVal;
          });

          node.addEventListener(
            "input",
            (e) => {
              const el = e || w.event;
              const lastK = keyArr[keyArr.length - 1];
              const obj = keyArr
                .slice(0, keyArr.length - 1)
                .reduce((obj, k) => obj[k], vm);
              console.log(obj[lastK]);
              obj[lastK] = el.target.value;
            },
            false
          );
        }
      }
      node.childNodes.forEach((child) => replace(child));
    }
    vm.$el.appendChild(oFrag);
  }

  class Dep {
    constructor() {
      this.adds = [];
    }
    addSub(watcher) {
      this.adds.push(watcher);
    }
    notify() {
      this.adds.forEach((watcher) => watcher.update());
    }
  }
  class Watcher {
    constructor(vm, key, cb) {
      this.vm = vm;
      (this.key = key), (this.cb = cb);
      Dep.target = this;
      key.split(".").reduce((obj, k) => obj[k], vm);
      Dep.target = null;
    }
    update() {
      const value = this.key.split(".").reduce((obj, k) => obj[k], this.vm);
      this.cb(value);
    }
  }
})(window, document);
